package cn.edu.scnu.socialapp.service.impl;

import cn.edu.scnu.socialapp.dao.mapper.UserMapper;
import cn.edu.scnu.socialapp.model.entity.User;
import cn.edu.scnu.socialapp.model.vo.ErrorCode;
import cn.edu.scnu.socialapp.model.vo.LoginUserVo;
import cn.edu.scnu.socialapp.model.vo.Result;
import cn.edu.scnu.socialapp.model.vo.UserVo;
import cn.edu.scnu.socialapp.model.vo.params.EmailCodeParams;
import cn.edu.scnu.socialapp.model.vo.params.LoginParams;
import cn.edu.scnu.socialapp.model.vo.params.RegisterParams;
import cn.edu.scnu.socialapp.service.ThreadService;
import cn.edu.scnu.socialapp.service.UserService;
import cn.edu.scnu.socialapp.utils.JWTUtils;
import cn.edu.scnu.socialapp.utils.TokenUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements UserService {
    private static final String[] FONT_NAMES = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312", "Arial", "Times New Roman"};
    // 加密盐
    private static final String SALT = "wangyi!@#$%^&*";
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private TokenUtils tokenUtils;
    @Autowired
    private ThreadService threadService;

    @Override
    public User findUserByUserId(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            user = new User();
            user.setNickname("佚名");
        }
        return user;
    }

    @Override
    public UserVo findUserVoByUserId(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            user = new User();
            user.setUserId(1L);
            user.setAvatar("/static/user/default.png");
            user.setNickname("博客用户");
        }
        return new UserVo(user.getNickname(), user.getAvatar(), user.getUserId());
    }

    /**
     * 1. token合法性校验 是否为空、解析是否成功、redis是否存在
     * 2. 如果校验失败，返回错误
     * 3. 如果成功，返回对应的结果LoginUserVO
     */
    @Override
    public Result findUserByToken(String token) {
        User user = tokenUtils.checkToken(token);
        if (user == null) {
            return Result.fail(ErrorCode.LOGIN_INVALID.getCode(),
                    ErrorCode.LOGIN_INVALID.getMessage());
        }
        LoginUserVo loginUserVo = new LoginUserVo(user.getUserId(),
                user.getPhoneNumber(),
                user.getNickname(),
                user.getAvatar());
        return Result.success(loginUserVo);
    }

    public User findUserByPhoneNumber(String phoneNumber) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhoneNumber, phoneNumber);
        queryWrapper.last("limit 1");
        return userMapper.selectOne(queryWrapper);
    }


    /**
     * 登录功能
     * 1. 检查参数是否合法
     * 2. 根据用户名和密码去user表中查询，是否存在
     * 3. 如果不存在，登录失败
     * 4. 如果存在，使用jwt生成token返回给前端
     * 5. token放入redis当中，redis token：user信息，设置过期时间
     * （登录认证的时候，先认证token字符串是否合法，去redis认证是否存在）
     */
    @Override
    public Result login(LoginParams loginParams) {
        // 校验 验证码
        String captcha = loginParams.getCaptcha();
        String captchaUUID = loginParams.getCaptchaUUID();
        String rightCaptcha = redisTemplate.opsForValue().get("PIC_CAPTCHA_" + captchaUUID);
        if (rightCaptcha == null) {
            return Result.fail(ErrorCode.CAPTCHA_EXPIRED.getCode(), ErrorCode.CAPTCHA_EXPIRED.getMessage());
        }
        if (!captcha.equalsIgnoreCase(rightCaptcha)) {
            return Result.fail(ErrorCode.CAPTCHA_WRONG.getCode(),
                    ErrorCode.CAPTCHA_WRONG.getMessage());
        }
        // 校验用户
        String account = loginParams.getPhoneNumber();
        String password = loginParams.getPassword();
        if (StringUtils.isBlank(account) || StringUtils.isBlank(password)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),
                    ErrorCode.PARAMS_ERROR.getMessage());
        }
        password = DigestUtils.md5Hex(password + SALT);
        User user = this.findUser(account, password);
        if (user == null) {
            return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(),
                    ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMessage());
        }
        // 校验通过，登录成功
        // 生成jwt的token
        String token = JWTUtils.createToken(user.getUserId());
        // 放入redis
        redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(user),
                1, TimeUnit.HOURS);
        threadService.updateUserLastLoginTime(user);
        return Result.success(token);
    }

    @Override
    public User findUser(String account, String password) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhoneNumber, account);
        queryWrapper.eq(User::getPassword, password);
        queryWrapper.select(User::getPhoneNumber, User::getUserId, User::getAvatar, User::getNickname);
        queryWrapper.last("limit 1");
        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public Result logout(String token) {
        redisTemplate.delete("TOKEN_" + token);
        return Result.success(null);
    }

    /**
     * 注册功能
     * 1. 判断参数 是否合法
     * 2. 判断账户是否存在，存在 返回账户已经被注册
     * 3. 不存在，注册用户
     * 4. 生成token
     * 5. 存入redis 并返回
     * 6. 注意加上事务，一旦中间的任何过程出现问题，注册的用户需要回滚
     */
    @Override
    public Result register(RegisterParams registerParams) {
        String phoneNumber = registerParams.getPhoneNumber();
        String password = registerParams.getPassword();
        String nickname = registerParams.getNickname();
        String email = registerParams.getEmail();
        if (StringUtils.isBlank(phoneNumber)
                || StringUtils.isBlank(password)
                || StringUtils.isBlank(nickname)
                || StringUtils.isBlank(email)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
        }
        User user = this.findUserByPhoneNumber(phoneNumber);
        if (user != null) {
            return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(), ErrorCode.ACCOUNT_EXIST.getMessage());
        }
        String redisKey = "EMAIL_CAPTCHA_" + registerParams.getEmailCodeUUID();
        System.out.println(redisKey);
        String rightCode = redisTemplate.opsForValue().get(redisKey);
        if (rightCode == null) {
            return Result.fail(ErrorCode.CAPTCHA_EXPIRED.getCode(), ErrorCode.CAPTCHA_EXPIRED.getMessage());
        }
        if (!registerParams.getEmailCode().equalsIgnoreCase(rightCode)) {
            return Result.fail(ErrorCode.CAPTCHA_WRONG.getCode(), ErrorCode.CAPTCHA_WRONG.getMessage());
        }
        user = new User();
        user.setPhoneNumber(phoneNumber);
        user.setPassword(DigestUtils.md5Hex(password + SALT));
        user.setNickname(nickname);
        user.setRegisterTime(LocalDateTime.now());
        user.setAvatar("/static/user/default.png");
        user.setStatus(1);
        user.setEmail(email);
        // mybatisplus默认雪花算法生成ID
        userMapper.insert(user);
        threadService.updateUserLastLoginTime(user);
        //token
        String token = JWTUtils.createToken(user.getUserId());
        redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(user), 1, TimeUnit.HOURS);
        return Result.success(token);
    }

    @Override
    public Result sendEmailCode(EmailCodeParams params) {
        String receiver = params.getEmail();
        String uuid = params.getUuid();
        System.out.println("Sending valid code to " + receiver);
        String captcha = String.format("%06d", new Random().nextInt(999999));
        try {
            HtmlEmail htmlEmail = new HtmlEmail();
            htmlEmail.setHostName("smtp.qq.com");
            htmlEmail.setCharset("utf-8");
            htmlEmail.addTo(receiver);        // 设置收件方
            htmlEmail.setFrom("wangyi9997@qq.com", "lynn");        // 设置发件方
            htmlEmail.setAuthentication("wangyi9997@qq.com", "ddzgamocukipbdgb");    // 设置授权码
            htmlEmail.setSubject("SocialApp验证码"); // 设置发送主题
            htmlEmail.setMsg("感谢您注册社交APP，您的邮箱验证码为：" + captcha + "，有效期5分钟，请尽快注册。"); // 设置发送内容
            htmlEmail.send(); // 进行发送
        } catch (EmailException e) {
            e.printStackTrace();
            return Result.fail(ErrorCode.EMAIL_CAPTCHA_SEND_EXCEPTION.getCode(), ErrorCode.EMAIL_CAPTCHA_SEND_EXCEPTION.getMessage());
        }
        System.out.println("Send successfully!");
        String redisKey = "EMAIL_CAPTCHA_" + uuid;
        redisTemplate.opsForValue().set(redisKey, captcha, 5, TimeUnit.MINUTES);
        return Result.success(null);
    }

    @Override
    // 生成指定长度的随机验证码
    public String generateRandomCaptcha(int length) {
        String chars = "abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            int index = (int) (Math.random() * chars.length());
            sb.append(chars.charAt(index));
        }
        return sb.toString();
    }

    @Override
    public BufferedImage generateCaptchaImage(String captcha) {
        int width = 100;
        int height = 40;
        int length = captcha.length();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D) image.getGraphics();
        // 设置背景色
        g.setColor(new Color(255, 255, 255));
        g.fillRect(0, 0, width, height);
        for (int i = 0; i < length; i++) {
            // 设置画笔颜色 -- 随机
            g.setColor(new Color(getRandom(0, 150), getRandom(0, 150), getRandom(0, 150)));
            // 设置字体
            int fontSize = 25;
            g.setFont(new Font(FONT_NAMES[getRandom(0, FONT_NAMES.length)], Font.BOLD, fontSize));
            // 旋转文字(-30~+30)
            int theta = getRandom(-30, 30);
            // 基数(验证码一个文字所占的空间大小)
            int base = 25;
            g.rotate(theta * Math.PI / 180, 7 + i * base, height - 8);
            g.drawString(String.valueOf(captcha.charAt(i)), 7 + i * base, height - 8);
            g.rotate(-theta * Math.PI / 180, 7 + i * base, height - 8);
        }
        // 画干扰线
        for (int i = 0; i < length + 2; i++) {
            // 设置画笔颜色 -- 随机
            g.setColor(new Color(getRandom(0, 150), getRandom(0, 150), getRandom(0, 150)));
            g.drawLine(getRandom(0, width), getRandom(0, height), getRandom(0, width), getRandom(0, height));
        }
        g.setColor(Color.gray);
        g.drawRect(0, 0, width - 1, height - 1);
        // 释放图形资源
        g.dispose();
        return image;
    }

    private int getRandom(int start, int end) {
        return new Random().nextInt(end - start) + start;
    }
}
